package com.example.ying.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.ying.common.R;
import com.example.ying.mapper.MovieMapper;
import com.example.ying.mapper.RateMapper;
import com.example.ying.model.domain.Movie;
import com.example.ying.model.domain.RatedMovie;
import com.example.ying.model.domain.RecommendMovie;
import com.example.ying.model.domain.neo4j.Category;
import com.example.ying.repository.neo4j.CategoryRepository;
import com.example.ying.repository.neo4j.MovieRepository;
import com.example.ying.service.MovieService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;


@Service
public class MovieServiceImpl extends ServiceImpl<MovieMapper, Movie> implements MovieService {
    @Autowired
    private MovieMapper movieMapper;
    @Autowired
    private MovieRepository movieRepository;
    @Autowired
    private CategoryRepository categoryRepository;
    @Autowired
    private RateMapper rateMapper;
    @Override
    public R allMovie() {
        return R.success(null, movieMapper.selectList(null));
    }

    @Override
    public R getAllCategory() {
        return R.success(null,categoryRepository.findAll());
    }

    @Override
    public R getMoviePage(int start) {
        PageInfo<Movie> pageInfo = new PageInfo<>();
        try (Page<Movie> moviePage = PageHelper.startPage(start, 10)) {
            pageInfo = new PageInfo<>(movieMapper.selectList(null));
        }catch (Exception e){
            System.out.println(e);
        }finally {
            PageHelper.clearPage();
        }
        return  R.success("查询成功",pageInfo);
    }

    @Override
    public R getMovieByCategory(String category) {
        List<com.example.ying.model.domain.neo4j.Movie> list = movieRepository.findMovieByCategory(category);
        List<Movie> movies = new ArrayList<>();
        for(com.example.ying.model.domain.neo4j.Movie movie:list){
            movies.add(movieMapper.selectBydoubanId(movie.getIndex()));
        }
        return R.success(null, movies);
    }

    @Override
    public R searchMovieByTitle(String title) {
        return null;
    }

    @Override
    public R getInfoById(int id){
        return R.success(null, movieMapper.selectById(id));
    }

    @Override
    public R getRatedMovieByUserId(String userId) {
        List<Integer> ratedIdList = rateMapper.getdoubanIdByUserId(userId);
        List<RatedMovie> ratedMovieList = new ArrayList<>();
        for(Integer id:ratedIdList){
            Movie movie = movieMapper.selectBydoubanId(id);
            ratedMovieList.add(new RatedMovie(movie.getTitle(),movie.getCategory(),rateMapper.getRateByUserIdAndMovieId(userId, id)));
        }
        return R.success(null, ratedMovieList);
    }

    @Override
    public R getInfoBydoubanId(int id) {
        return R.success(null, movieMapper.selectBydoubanId(id));
    }

    @Override
    public R getRecommendMovieByUserId(String userId) {
        // 获取用户评分最高的两部电影
        List<Integer> top2Movies = rateMapper.getTop2MoviesByUserId(userId);

        // 获取关联用户(给相同电影评分了的用户)
        List<List<String>> relatedUsers = new ArrayList<>();
        for (Integer i : top2Movies) {
            List<String> relatedUser = new ArrayList<>();
            relatedUser.addAll(rateMapper.getUserIdByMovieId(i));
            relatedUsers.add(relatedUser);
        }
        //求relatedUsers的交集
        List<String> intersection = relatedUsers.get(0);
        for(int i = 1; i < relatedUsers.size(); i++){
            intersection.retainAll(relatedUsers.get(i));
        }
        intersection.remove(userId);

        // 余弦相似度计算相似用户
        List<String> similarUsers = new ArrayList<>();

        // 用户评分向量
        List<Integer> userRating = new ArrayList<>();
        for (Integer i : top2Movies) {
            userRating.add(rateMapper.getRateByUserIdAndMovieId(String.valueOf(userId), i));
        }

        for (String relatedUserId : intersection){
            // 相似用户评分向量
            List<Integer> relatedUserRating = new ArrayList<>();
            for (Integer i : top2Movies) {
                relatedUserRating.add(rateMapper.getRateByUserIdAndMovieId(relatedUserId, i));
            }
            // 计算余弦相似度
            double similarity = cosineSimilarity(userRating, relatedUserRating);
            //设定阈值, 相似度大于99%则认为相似
            if (similarity > 0.99) {
                similarUsers.add(relatedUserId);
            }
        }

        // 获取电影的类型
        Set<Category> categories = new HashSet<>();
        for (Integer i : top2Movies) {
            categories.addAll(categoryRepository.findCategoryByMovieIndex(i));
        }

        //获取与电影类型相似的电影
        Set<com.example.ying.model.domain.neo4j.Movie> similarMovies = new HashSet<>();
        for (Category category : categories) {
            similarMovies.addAll(movieRepository.findMovieByCategoryIndex(category.getIndex()));
        }

        //电影的平均评分集
        Map<Integer, Double> movieAverageRating = new HashMap<>();

        //遍历预选集合
        for (com.example.ying.model.domain.neo4j.Movie m : similarMovies) {
            //获取推荐该电影的相似用户个数
            int recommendUserCount = 0;
            //获取总评分
            int totalScore = 0;
            for (String s : similarUsers){
                if(rateMapper.existRateByUserIdAndMovieId(s,m.getIndex()) !=null ){
                    recommendUserCount++;
                    totalScore += rateMapper.getRateByUserIdAndMovieId(s,m.getIndex());
                }
            }
            //计算平均评分
            double avgScore = 0.0;
            if(recommendUserCount!=0) {
                avgScore = totalScore / recommendUserCount;
                movieAverageRating.put(m.getIndex(), avgScore);
            }
        }

        //根据评分排序
        List<Map.Entry<Integer, Double>> list1 = new ArrayList<>(movieAverageRating.entrySet());
        Collections.sort(list1, (o1, o2) -> o2.getValue().compareTo(o1.getValue()));

        List<RecommendMovie> recommendMovies = new ArrayList<>();
        for (Map.Entry<Integer, Double> entry : list1){
            Movie m = movieMapper.selectBydoubanId(entry.getKey());
            RecommendMovie recommendMovie = new RecommendMovie(entry.getKey(),m.getTitle(),m.getPic(),entry.getValue());
            recommendMovies.add(recommendMovie);
        }

        return R.success(null, recommendMovies);
    }

    // 余弦相似度是两个向量夹角的余弦值，它是两个向量的夹角的余弦值与这两个向量的模的乘积的倒数
    public static double cosineSimilarity(List<Integer> v1, List<Integer> v2) {
        // 计算两个向量的模
        double magnitude1 = magnitude(v1);
        double magnitude2 = magnitude(v2);

        // 计算两个向量的夹角的余弦值
        double dotProduct = dotProduct(v1, v2);

        return dotProduct / (magnitude1 * magnitude2);
    }

    // 计算向量的模
    private static double magnitude(List<Integer> v) {
        double sum = 0;
        for (int i : v) {
            sum += i * i;
        }
        return Math.sqrt(sum);
    }

    // 计算两个向量的点积
    private static double dotProduct(List<Integer> v1, List<Integer> v2) {
        double sum = 0;
        for (int i = 0; i < v1.size(); i++) {
            sum += v1.get(i) * v2.get(i);
        }
        return sum;
    }
}
